package cz.matasek.dbLogger.gui;

import cz.matasek.dbLogger.LoggingAvailability;
import cz.matasek.dbLogger.Util;
import cz.matasek.dbLogger.model.Column;
import cz.matasek.dbLogger.model.Database;
import cz.matasek.dbLogger.model.Table;
import cz.matasek.dbLogger.model.TriggeringEvents;
import cz.matasek.dbLogger.model.TriggeringEvents.Event;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.swing.table.AbstractTableModel;

/**
 * The panel contains basic information about a table.
 *
 * @author Luboš Matásek
 */
public class TableInfoPanel extends javax.swing.JPanel implements Refreshable {

    /**
     * The class provides table model for listing columns of any database table.
     */
    private class ColumnsTableModel extends AbstractTableModel {

        /**
         * Serial version UID.
         */
        private static final long serialVersionUID = -8154869771622860940L;
        /**
         * Column headings.
         */
        private String[] columnHeadings = {"Column name", "Data type", "Logged?"};
        /**
         * List of columns.
         */
        private List<Column> columns;

        /**
         * Creates a new <code>ColumnsTableModel</code> for specified table.
         *
         * @param table the table, whose columns model is created
         */
        public ColumnsTableModel(Table table) {
            columns = new ArrayList<Column>(table.getColumns());
            Collections.sort(columns);
        }

        /**
         * Returns the number of rows in the model.
         *
         * @return the number of rows in the model
         */
        @Override
        public int getRowCount() {
            return columns.size();
        }

        /**
         * Returns the number of columns in the model.
         *
         * @return the number of columns in the model
         */
        @Override
        public int getColumnCount() {
            return columnHeadings.length;
        }

        /**
         * Returns the name of the column at <code>columnIndex</code>.
         *
         * @param columnIndex the index of the column
         * @return the name of the column
         */
        @Override
        public String getColumnName(int columnIndex) {
            return columnHeadings[columnIndex];
        }

        /**
         * Returns the most specific superclass for all the cell values in the
         * column.
         *
         * @param columnIndex the index of the column
         * @return the common ancestor class of the object values in the model
         */
        @Override
        public Class<?> getColumnClass(int columnIndex) {
            switch (columnIndex) {
                case 0:
                case 1:
                    return String.class;
                case 2:
                    return Boolean.class;
                default:
                    assert false;
                    return Object.class;
            }
        }

        /**
         * Returns <code>true</code> if the cell at <code>rowIndex</code> and
         * <code>columnIndex</code> is editable.
         *
         * @param rowIndex the row whose value to be queried
         * @param columnIndex the column whose value to be queried
         * @return <code>true</code> if the cell is editable; <code>false</code>
         * otherwise
         */
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return false;
        }

        /**
         * Returns the value for the cell at <code>rowIndex</code> and
         * <code>columnIndex</code>.
         *
         * @param rowIndex the row whose value to be queried
         * @param columnIndex the column whose value to be queried
         * @return the value Object at the specified cell
         */
        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            switch (columnIndex) {
                case 0:
                    return columns.get(rowIndex).getName();
                case 1:
                    return columns.get(rowIndex).getDataTypeName();
                case 2:
                    return columns.get(rowIndex).isLogged();
                default:
                    assert false;
                    return null;
            }
        }
    }
    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 7127072906044158666L;
    /**
     * The table, this info panel belongs to.
     */
    private Table table;
    /**
     * Name of the table, this info panel belongs to.
     */
    private String tableName;
    /**
     * Database, where the table is stored.
     */
    private Database database;
    /**
     * "Create history table" label.
     */
    private static String CREATE_HISTORY_TABLE = "Create history table";
    /**
     * "Drop history table" label.
     */
    private static String DROP_HISTORY_TABLE = "Drop history table";
    /**
     * "Start logging" label.
     */
    private static String START_LOGGING = "Start logging";
    /**
     * "Stop logging" label.
     */
    private static String STOP_LOGGING = "Stop logging";

    /**
     * Creates a new <code>TableInfoPanel</code>.
     *
     * @param tableName name of the table, this info panel belongs to
     * @param database database, where the table is stored
     */
    public TableInfoPanel(String tableName, Database database) {
        this.database = database;
        this.tableName = tableName;
        this.table = database.getTable(tableName);

        initComponents();

        refresh();

        if (!database.getConnection().supportOnUpdateTriggers()) {
            _onUpdateColumns.setVisible(false);
        }
    }

    /**
     * Sets checkbox to show if the specified triggering operation fires
     * logging.
     * 
     * @param event triggering event to be set
     * @param firesLogging <code>true</code> if event fires logging;
     * <code>false</code> otherwise
     */
    private void setTriggeringEvent(Event event, boolean firesLogging) {
        switch (event) {
            case INSERT:
                _insertTriggeringEvent.setSelected(firesLogging);
                break;
            case UPDATE:
                _updateTriggeringEvent.setSelected(firesLogging);
                break;
            case DELETE:
                _deleteTriggeringEvent.setSelected(firesLogging);
                break;
        }
    }

    @Override
    public void refresh() {
        table = database.getTable(tableName);
        _tableColumns.setModel(new ColumnsTableModel(table));

        // changes logging
        if (table.isLogged()) {
            TriggeringEvents loggedEvents = table.getLoggedEvents();
            for (Event event : Event.values()) {
                setTriggeringEvent(event, loggedEvents.hasEvent(event));
            }

            if (database.getConnection().supportOnUpdateTriggers()) {
                Collection<String> onUpdateColumns =
                        loggedEvents.getOnUpdateColumns();
                if (onUpdateColumns.isEmpty()) {
                    _onUpdateColumns.setText(new String());
                } else {
                    String[] columnsArray = onUpdateColumns.toArray(new String[1]);
                    Arrays.sort(columnsArray);

                    _onUpdateColumns.setText(Util.implode("\n", columnsArray));
                }
            }

            _loggingButton.setText(STOP_LOGGING);
        } else {
            for (Event event : Event.values()) {
                setTriggeringEvent(event, false);
            }
            _onUpdateColumns.setText(new String());

            _loggingButton.setText(START_LOGGING);
        }

        // history table
        if (table.hasHistoryTable()) {
            _historyTableButton.setText(DROP_HISTORY_TABLE);
        } else {
            _historyTableButton.setText(CREATE_HISTORY_TABLE);
        }

        if (database.getLoggingAvailability() == LoggingAvailability.AVAILABLE) {
            _historyTableButton.setEnabled(true);
            _loggingButton.setEnabled(true);
        } else {
            _historyTableButton.setEnabled(false);
            _loggingButton.setEnabled(false);
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        _tableColumnsScrollPane = new javax.swing.JScrollPane();
        _tableColumns = new javax.swing.JTable();
        jPanel2 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        _insertTriggeringEvent = new javax.swing.JCheckBox();
        _updateTriggeringEvent = new javax.swing.JCheckBox();
        _deleteTriggeringEvent = new javax.swing.JCheckBox();
        _onUpdateColumns = new javax.swing.JTextArea();
        jLabel2 = new javax.swing.JLabel();
        _loggingButton = new javax.swing.JButton();
        _historyTableButton = new javax.swing.JButton();
        jLabel3 = new javax.swing.JLabel();

        setMinimumSize(new java.awt.Dimension(495, 345));
        setPreferredSize(new java.awt.Dimension(495, 345));

        _tableColumns.setModel(new ColumnsTableModel(table));
        _tableColumnsScrollPane.setViewportView(_tableColumns);

        jLabel1.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel1.setText("Logging");

        _insertTriggeringEvent.setText("Insert");
        _insertTriggeringEvent.setEnabled(false);

        _updateTriggeringEvent.setText("Update");
        _updateTriggeringEvent.setEnabled(false);

        _deleteTriggeringEvent.setText("Delete");
        _deleteTriggeringEvent.setEnabled(false);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(_insertTriggeringEvent)
                    .addComponent(_updateTriggeringEvent)
                    .addComponent(_deleteTriggeringEvent))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addComponent(_insertTriggeringEvent)
                .addGap(18, 18, 18)
                .addComponent(_updateTriggeringEvent)
                .addGap(18, 18, 18)
                .addComponent(_deleteTriggeringEvent))
        );

        _onUpdateColumns.setBackground(new java.awt.Color(244, 244, 244));
        _onUpdateColumns.setColumns(20);
        _onUpdateColumns.setRows(5);
        _onUpdateColumns.setBorder(javax.swing.BorderFactory.createTitledBorder("On update columns"));

        jLabel2.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel2.setText("History table");

        _loggingButton.setText("Create Table");
        _loggingButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                _loggingButtonActionPerformed(evt);
            }
        });

        _historyTableButton.setText("Start logging");
        _historyTableButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                _historyTableButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(_onUpdateColumns, javax.swing.GroupLayout.PREFERRED_SIZE, 196, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addComponent(_loggingButton, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGap(204, 204, 204)
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(_historyTableButton, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel2))))
                .addContainerGap(21, Short.MAX_VALUE))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(_loggingButton)
                    .addComponent(_historyTableButton))
                .addGap(18, 18, 18)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(_onUpdateColumns, javax.swing.GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE)
                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jLabel3.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel3.setText("Columns");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(10, 10, 10)
                        .addComponent(jLabel3))
                    .addComponent(_tableColumnsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 475, Short.MAX_VALUE)
                    .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(11, 11, 11)
                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jLabel3)
                .addGap(8, 8, 8)
                .addComponent(_tableColumnsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 97, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    /**
     * Performs history table action (either creates or drops it).
     *
     * @param evt action event
     */
    private void _historyTableButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event__historyTableButtonActionPerformed
        if (table.hasHistoryTable()) {
            database.getConnection().stopLoggingHistory(tableName);
        } else {
            database.getConnection().startLoggingHistory(tableName);
        }

        database.updateMetadata();
        refresh();

        // refreshes all
        Gui.getInstance().refresh();
}//GEN-LAST:event__historyTableButtonActionPerformed

    /**
     * Performs logging table action (either creates or drops it).
     *
     * @param evt action event
     */
    private void _loggingButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event__loggingButtonActionPerformed
        if (table.isLogged()) {
            database.getConnection().dropLoggingTrigger(tableName);
        } else {
            javax.swing.JDialog dialog = new LoggingSettingsDialog(
                    Gui.getInstance(), true, database, table,
                    database.getConnection().supportOnUpdateTriggers());
            dialog.setVisible(true);
        }

        database.updateMetadata();
        refresh();

        Gui.getInstance().refresh();
}//GEN-LAST:event__loggingButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JCheckBox _deleteTriggeringEvent;
    private javax.swing.JButton _historyTableButton;
    private javax.swing.JCheckBox _insertTriggeringEvent;
    private javax.swing.JButton _loggingButton;
    private javax.swing.JTextArea _onUpdateColumns;
    private javax.swing.JTable _tableColumns;
    private javax.swing.JScrollPane _tableColumnsScrollPane;
    private javax.swing.JCheckBox _updateTriggeringEvent;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    // End of variables declaration//GEN-END:variables
}
